package com.ahoi.common.utils;

import com.ahoi.common.constant.CommonEnum;
import com.ahoi.model.BookInfo;
import com.ahoi.model.DownUrl;
import com.ahoi.service.BookInfoService;
import com.ahoi.service.DownUrlService;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * author: ahoi
 * date: 2018/8/24 17:57
 * description:
 */
@Component
public class ScanBookUtils {

    @Autowired
    private BookInfoService bookInfoService;
    private static BookInfoService sBookInfoService;

    @Autowired
    private DownUrlService downUrlService;
    private static DownUrlService sDownUrlService;

    // 小说列表页面地址
    private static String BOOK_LIST_URL = "https://www.xiashu.la/type/nan_0_0_allvisit_{pageNum}.html";
    // 小说详情页面地址
    private static String BOOK_DETAIL_URL = "https://www.xiashu.la/{id}";
    // 小说下载列表页面地址
    private static String BOOK_DOWNLOAD_URL = "https://www.xiashu.la/{id}/down";
    // 静态实例
    private static ScanBookUtils instance = new ScanBookUtils();
    // 收集ID，书名，图片
    private static List<BookInfo> bookBaseInfos = new ArrayList<>();
    // 存放用于查询小说详细信息
    private static List<Integer> bookIdToQueryDetail = new ArrayList<>();
    // 存放用于查询小说下载地址
    private static List<Integer> bookIdToQueryDownUrl = new ArrayList<>();

    private int max = 100;
    private int getOne = 1;
    private int insert = 1;
    private int update = 1;
    private int down = 1;
    // 线程池
    private static ExecutorService executor;

    private ScanBookUtils() {

    }

    public static ScanBookUtils getInstance() {
        return instance;
    }


    class Producer implements Runnable {
        @Override
        public void run() {
            try {
                init();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class CustomerBase implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    saveBaseInfo();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class CustomerDetail implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    saveDetailInfo();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class CustomerDownUrl implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    saveDownUrl();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    // 初始化，获取总页数
    private synchronized void init() throws Exception {
        String url = BOOK_LIST_URL.replace("{pageNum}", String.valueOf(0));
        Document doc = Jsoup.connect(url).get();

        // 获取总页数和总条数
        String pageInfo = doc.select(".pagination span").text();
        if (StringUtils.isNotEmpty(pageInfo)) {
            // 共5263页 157870条
            pageInfo = pageInfo.replaceAll("[共页条]", "");
            String[] split = pageInfo.split(" ");
            if (split.length == 2) {
                String totalPage = split[0];

                // 开始扫描每一页获取小说
                getBooks(Integer.parseInt(totalPage));
            }
        }
    }

    // 生产者：开始扫描分页获取小说
    private void getBooks(Integer totalPage) throws Exception {
        System.out.println(DateUtils.getDateTimeString() + " - page to: 1");
        for (int i = 1; i < totalPage; i++) {
            if (i == 50) {
                System.out.println(DateUtils.getDateTimeString() + " - page to: " + i);
            }
            Document doc = JsoupUtils.loadURL(BOOK_LIST_URL.replace("{pageNum}", String.valueOf(i)));

            BookInfo bookInfo;
            Elements divList = doc.select("#waterfall .item");
            for (Element div : divList) {
                bookInfo = new BookInfo();
                bookInfo.setId(Integer.parseInt(div.select(".title a").attr("href").replaceAll("/", "")));
                bookInfo.setTitle(div.select(".title a").text());
                bookInfo.setImgUrl(div.select(".pic img").attr("data-original"));
                bookBaseInfos.add(bookInfo);
//                System.out.println("getOne: " + getOne++);

//                // 生产到一定数量，就等待消费者消费
//                while (bookBaseInfos.size() >= max) {
//                    wait();
//                }
//                notifyAll();
            }
        }
    }

    // 消费者：保存小说基本信息。同时也是第二个生产者，准备给第二个消费者保存详细信息
    private synchronized void saveBaseInfo() throws Exception {
        // 如果已经消费完了，并且生产者已暂停，就唤醒生产者
        while (bookBaseInfos.size() == 0 || bookIdToQueryDetail.size() >= max || bookIdToQueryDownUrl.size() >= max) {
            wait();
        }
        notifyAll();
        if (bookBaseInfos.size() > 0) {
            BookInfo one = bookBaseInfos.remove(0);
//            bookIdToQueryDetail.add(one.getId());
            // 数据库不存在才插入
            if (!sBookInfoService.checkExists(one.getId())) {
                sBookInfoService.insertOne(one.getId(), one.getTitle(), one.getImgUrl());
//                System.out.println("insert: " + Thread.currentThread().getName() + "-" + insert++);
            }
        }
    }

    // 消费者：保存小说详细信息
    private synchronized void saveDetailInfo() throws Exception {
        while (bookIdToQueryDetail.size() == 0 || bookIdToQueryDownUrl.size() >= max) {
            wait();
        }
        notifyAll();
        if (bookIdToQueryDetail.size() > 0) {
            BookInfo one = new BookInfo();
            Integer bookId = bookIdToQueryDetail.remove(0);
            Document doc = JsoupUtils.loadURL(BOOK_DETAIL_URL.replace("{id}", String.valueOf(bookId)));

            // ID
            one.setId(bookId);

            // 简介
            doc.select("#aboutbook a").remove();
            doc.select("#aboutbook h3").remove();
            one.setInfoDesc(doc.select("#aboutbook").html());

            // 作者
            one.setAuthor(doc.select("#infobox .username a").text());

            // 最新章节
            one.setLastestCatalog(doc.select("#info .tag a").text());

            // 最后更新时间
            one.setLastUpdateTime(new Timestamp(DateUtils.createDate(doc.select("#info .tag .time").text()).getTime()));

            // 作品类别
            one.setType(doc.select("#mainright .infonum ul li:eq(0)").text().replaceAll("作品类别([：:])", ""));

            // 总章节
            one.setTotalCatalog(Integer.parseInt(doc.select("#mainright .infonum ul li:eq(1)").text().replaceAll("[^\\d]", "")));

            // 标签
            one.setAlias(doc.select("#mainright .infonum ul li:eq(5)").text().replaceAll("标签([：:])", ""));

            // 更新入库
            bookIdToQueryDownUrl.add(bookId);
//            sBookInfoService.updateDetail(one);
//            System.out.println("update: " + Thread.currentThread().getName() + "-id: " + bookId + "-" + update++);
        }
    }

    // 保存下载地址
    private synchronized void saveDownUrl() throws Exception {
        while (bookIdToQueryDownUrl.size() == 0) {
            wait();
        }
        notifyAll();
        if (bookIdToQueryDownUrl.size() > 0) {
            List<DownUrl> downUrlList = new ArrayList<>();
            DownUrl downUrl;
            Integer bookId = bookIdToQueryDownUrl.remove(0);
            Document doc = JsoupUtils.loadURL(BOOK_DOWNLOAD_URL.replace("{id}", String.valueOf(bookId)));
            Elements downs = doc.getElementById("downlist").getElementsByTag("ul");
            Elements zips = downs.get(0).children();
            for (Element ele : zips) {
                Elements eleA = ele.getElementsByTag("a");
                if (!eleA.isEmpty()) {
                    String down = eleA.get(0).attr("href");
                    String url = JsoupUtils.loadURL(down).select("a").attr("href");
                    downUrl = new DownUrl();
                    downUrl.setBookId(bookId);
                    downUrl.setUrl(url);
                    downUrl.setUrlPrev(down);
                    downUrl.setType(CommonEnum.YES.getCode());
                    downUrlList.add(downUrl);
                }
            }
            Elements txts = downs.get(1).children();
            for (Element ele : txts) {
                Elements eleA = ele.getElementsByTag("a");
                if (!eleA.isEmpty()) {
                    String down = eleA.get(0).attr("href");
                    String url = JsoupUtils.loadURL(down).select("a").attr("href");
                    downUrl = new DownUrl();
                    downUrl.setBookId(bookId);
                    downUrl.setUrl(url);
                    downUrl.setUrlPrev(down);
                    downUrl.setType(CommonEnum.NO.getCode());
                    downUrlList.add(downUrl);
                }
            }

            // 保存入库
            for (DownUrl model : downUrlList) {
//                System.out.println("down: " + Thread.currentThread().getName() + "-id: " + bookId + "-" + down++);
                if (sDownUrlService.checkExists(model)) {
                    sDownUrlService.updateOne(model);
                } else {
                    sDownUrlService.insertOne(model);
                }
            }
        }
    }

//    @PostConstruct
    public void start() {
        sBookInfoService = bookInfoService;
        sDownUrlService = downUrlService;

        System.setProperty("https.proxySet", "true");
        System.getProperties().put("https.proxyHost", "192.168.1.16");
        System.getProperties().put("https.proxyPort", "808");

        new Thread(() -> {
            try {
                getBooks(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                getBooks(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

//        Producer producer = new Producer();
//        CustomerBase customerBase1 = new CustomerBase();
//        CustomerDetail customerDetail = new CustomerDetail();
//        CustomerDownUrl customerDownUrl = new CustomerDownUrl();

//        executor = Executors.newFixedThreadPool(10);
//        executor.execute(producer);
//        executor.execute(customerBase1);
//        executor.execute(customerDetail);
//        executor.execute(customerDownUrl);
    }

}
